#!/bin/bash

function die_with_error {
    local err_msg

    err_msg="$1"

    echo "$err_msg" >&2
    exit 1
}

function xapi_plugin_location {
    for PLUGIN_DIR in "/etc/xapi.d/plugins/" "/usr/lib/xcp/plugins/" "/usr/lib/xapi/plugins" "/usr/lib64/xapi/plugins"; do
        if [ -d $PLUGIN_DIR ]; then
            echo $PLUGIN_DIR
            return 0
        fi
    done
    return 1
}

function create_directory_for_kernels {
    if [ -d "/boot/guest" ]; then
        echo "INFO: /boot/guest directory already exists, using that" >&2
    else
        local local_path
        local_path="$(get_local_sr_path)/os-guest-kernels"
        mkdir -p $local_path
        ln -s $local_path /boot/guest
    fi
}

function create_directory_for_images {
    if [ -d "/images" ]; then
        echo "INFO: /images directory already exists, using that" >&2
    else
        local local_path
        local_path="$(get_local_sr_path)/os-images"
        mkdir -p $local_path
        ln -s $local_path /images
    fi
}

function get_local_sr {
    xe pool-list params=default-SR minimal=true
}

function get_local_sr_path {
    pbd_path="/var/run/sr-mount/$(get_local_sr)"
    pbd_device_config_path=`xe pbd-list sr-uuid=$(get_local_sr) params=device-config | grep " path: "`
    if [ -n "$pbd_device_config_path" ]; then
        pbd_uuid=`xe pbd-list sr-uuid=$(get_local_sr) minimal=true`
        pbd_path=`xe pbd-param-get uuid=$pbd_uuid param-name=device-config param-key=path || echo ""`
    fi
    echo $pbd_path
}

function find_ip_by_name {
    local guest_name="$1"
    local interface="$2"

    local period=10
    local max_tries=10
    local i=0

    while true; do
        if [ $i -ge $max_tries ]; then
            echo "Timeout: ip address for interface $interface of $guest_name"
            exit 11
        fi

        ipaddress=$(xe vm-list --minimal \
                    name-label=$guest_name \
                    params=networks | sed -ne "s,^.*${interface}/ip: \([0-9.]*\).*\$,\1,p")

        if [ -z "$ipaddress" ]; then
            sleep $period
            i=$((i+1))
        else
            echo $ipaddress
            break
        fi
    done
}

function _vm_uuid {
    local vm_name_label

    vm_name_label="$1"

    xe vm-list name-label="$vm_name_label" --minimal
}

function _create_new_network {
    local name_label
    name_label=$1

    xe network-create name-label="$name_label"
}

function _multiple_networks_with_name {
    local name_label
    name_label=$1

    # A comma indicates multiple matches
    xe network-list name-label="$name_label" --minimal | grep -q ","
}

function _network_exists {
    local name_label
    name_label=$1

    ! [ -z "$(xe network-list name-label="$name_label" --minimal)" ]
}

function _bridge_exists {
    local bridge
    bridge=$1

    ! [ -z "$(xe network-list bridge="$bridge" --minimal)" ]
}

function _network_uuid {
    local bridge_or_net_name
    bridge_or_net_name=$1

    if _bridge_exists "$bridge_or_net_name"; then
        xe network-list bridge="$bridge_or_net_name" --minimal
    else
        xe network-list name-label="$bridge_or_net_name" --minimal
    fi
}

function add_interface {
    local vm_name_label
    local bridge_or_network_name

    vm_name_label="$1"
    bridge_or_network_name="$2"
    device_number="$3"

    local vm
    local net

    vm=$(_vm_uuid "$vm_name_label")
    net=$(_network_uuid "$bridge_or_network_name")
    xe vif-create network-uuid=$net vm-uuid=$vm device=$device_number
}

function setup_network {
    local bridge_or_net_name
    bridge_or_net_name=$1

    if ! _bridge_exists "$bridge_or_net_name"; then
        if _network_exists "$bridge_or_net_name"; then
            if _multiple_networks_with_name "$bridge_or_net_name"; then
                cat >&2 << EOF
ERROR: Multiple networks found matching name-label to "$bridge_or_net_name"
please review your XenServer network configuration / localrc file.
EOF
                exit 1
            fi
        else
            _create_new_network "$bridge_or_net_name"
        fi
    fi
}

function bridge_for {
    local bridge_or_net_name
    bridge_or_net_name=$1

    if _bridge_exists "$bridge_or_net_name"; then
        echo "$bridge_or_net_name"
    else
        xe network-list name-label="$bridge_or_net_name" params=bridge --minimal
    fi
}

function xenapi_ip_on {
    local bridge_or_net_name
    bridge_or_net_name=$1

    ip -4 addr show $(bridge_for "$bridge_or_net_name") |\
    awk '/inet/{split($2, ip, "/"); print ip[1];}'
}

function xenapi_is_listening_on {
    local bridge_or_net_name
    bridge_or_net_name=$1

    ! [ -z $(xenapi_ip_on "$bridge_or_net_name") ]
}

function parameter_is_specified {
    local parameter_name
    parameter_name=$1

    compgen -v | grep "$parameter_name"
}

function append_kernel_cmdline {
    local vm_name_label
    local kernel_args

    vm_name_label="$1"
    kernel_args="$2"

    local vm
    local pv_args

    vm=$(_vm_uuid "$vm_name_label")
    pv_args=$(xe vm-param-get param-name=PV-args uuid=$vm)
    xe vm-param-set PV-args="$pv_args $kernel_args" uuid=$vm
}

function destroy_all_vifs_of {
    local vm_name_label

    vm_name_label="$1"

    local vm

    vm=$(_vm_uuid "$vm_name_label")
    IFS=,
    for vif in $(xe vif-list vm-uuid=$vm --minimal); do
        xe vif-destroy uuid="$vif"
    done
    unset IFS
}

function have_multiple_hosts {
    xe host-list --minimal | grep -q ","
}

function attach_network {
    local bridge_or_net_name

    bridge_or_net_name="$1"

    local net
    local host

    net=$(_network_uuid "$bridge_or_net_name")
    host=$(xe host-list --minimal)

    xe network-attach uuid=$net host-uuid=$host
}

function set_vm_memory {
    local vm_name_label
    local memory

    vm_name_label="$1"
    memory="$2"

    local vm

    vm=$(_vm_uuid "$vm_name_label")

    xe vm-memory-limits-set \
        static-min=${memory}MiB \
        static-max=${memory}MiB \
        dynamic-min=${memory}MiB \
        dynamic-max=${memory}MiB \
        uuid=$vm
}

function max_vcpus {
    local vm_name_label

    vm_name_label="$1"

    local vm
    local host
    local cpu_count

    host=$(xe host-list --minimal)
    vm=$(_vm_uuid "$vm_name_label")

    cpu_count=$(xe host-param-get \
        param-name=cpu_info \
        uuid=$host |
        sed -e 's/^.*cpu_count: \([0-9]*\);.*$/\1/g')

    if [ -z "$cpu_count" ]; then
        # get dom0's vcpu count
        cpu_count=$(cat /proc/cpuinfo | grep processor | wc -l)
    fi

    # Assert cpu_count is not empty
    [ -n "$cpu_count" ]

    # Assert ithas a numeric nonzero value
    expr "$cpu_count" + 0

    # 8 VCPUs should be enough for devstack VM; avoid using too
    # many VCPUs:
    # 1. too many VCPUs may trigger a kernel bug which result VM
    #    not able to boot:
    #    https://kernel.googlesource.com/pub/scm/linux/kernel/git/wsa/linux/+/e2e004acc7cbe3c531e752a270a74e95cde3ea48
    # 2. The remaining CPUs can be used for other purpose:
    #    e.g. boot test VMs.
    MAX_VCPUS=8
    if [ $cpu_count -ge $MAX_VCPUS ]; then
        cpu_count=$MAX_VCPUS
    fi

    xe vm-param-set uuid=$vm VCPUs-max=$cpu_count
    xe vm-param-set uuid=$vm VCPUs-at-startup=$cpu_count
}

function get_domid {
    local vm_name_label

    vm_name_label="$1"

    xe vm-list name-label="$vm_name_label" params=dom-id minimal=true
}

function install_conntrack_tools {
    local xs_host
    local xs_ver_major
    local centos_ver
    local conntrack_conf
    xs_host=$(xe host-list --minimal)
    xs_ver_major=$(xe host-param-get uuid=$xs_host param-name=software-version param-key=product_version_text_short | cut -d'.' -f 1)
    if [ $xs_ver_major -gt 6 ]; then
        # Only support conntrack-tools in Dom0 with XS7.0 and above
        if [ ! -f /usr/sbin/conntrackd ]; then
            sed -i s/#baseurl=/baseurl=/g /etc/yum.repos.d/CentOS-Base.repo
            centos_ver=$(yum version nogroups |grep Installed | cut -d' ' -f 2 | cut -d'/' -f 1 | cut -d'-' -f 1)
            yum install -y --enablerepo=base --releasever=$centos_ver conntrack-tools
            # Backup conntrackd.conf after install conntrack-tools, use the one with statistic mode
            mv /etc/conntrackd/conntrackd.conf /etc/conntrackd/conntrackd.conf.back
            conntrack_conf=$(find /usr/share/doc -name conntrackd.conf |grep stats)
            cp $conntrack_conf /etc/conntrackd/conntrackd.conf
        fi
        service conntrackd restart
    fi
}
